# This file is part of PyMXit, a Python library implementing the basic
# functionality of the MXit protocol.
#
# Copyright (C) 2008 Ralf Kistner <ralf.kistner@gmail.com>
# 
# PyMXit is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# PyMXit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with PyMXit.  If not, see <http://www.gnu.org/licenses/>.

from mxit.commands import Client, Server, Binary
from mxit.handles import *

from mxit.server.listener import Listener
from mxit.server.users import *
from mxit.server.handlers import HandleManager

def parse(data):
    tokens = {}
    for s in data.split("\x00"):
        e = s.find("=")
        if e == -1:
            continue
        name = s[:e]
        value = s[e+1:]
        tokens[name] = parse_value(value)
    
    return tokens

def parse_value(value):
    return value.split('\1')
    #return map(lambda s: s.split('\1'), value.split('\0'))


def encode_string(string):
    if hasattr(string, 'encode'):
        return string.encode("ISO-8859-1")
    else:
        return str(string).encode("ISO-8859-1")
    
def build_command(data):
    """ Build a command for sending. """
    cmds = []
    for cmd in data:
        bits = []
        for a in cmd:
            if isinstance(a, (tuple, list)):
                bits.append('\1'.join(map(encode_string, a)))
            else:
                bits.append(encode_string(a))
        cmds.append('\0'.join(bits))
    f = '\2'.join(cmds)
    
    return "ln=%d\0%s" % (len(f), f)

    
class ServerConnection:
    def __init__(self, socket, userlist):
        self.socket = socket
        self.buffer = ""
        self.req = 0
        
        self.userlist = userlist    #TODO
        self.user = None
        self.handle_manager = HandleManager(self)
        
        
    
    def append(self, data):
        self.buffer += data
        if self.req == 0:
            self.recalcreq()
            
        print self.req, repr(self.buffer)
            
        if self.req > 0 and len(self.buffer) >= self.req:
            self.got_data(self.buffer[:self.req])
            self.buffer = self.buffer[self.req:]
            self.recalcreq()
            
    def recalcreq(self):
        if len(self.buffer) > 4 and '\x00' in self.buffer:
            if not self.buffer.startswith("ln="):
                print "invalid buffer:", repr(self.buffer)
                self.buffer = ""
            end = self.buffer.index('\x00')
            self.req = int(self.buffer[3:end]) + end + 1
        else:
            self.req = 0
    
    def send_raw(self, data):
        self.socket.send(data)
    
    def send(self, data):
        self.send_multiple([data])
        
    def send_multiple(self, data):
        print "SENDING:", data
        self.send_raw(build_command(data))
    
    def send_command(self, cmd, data):
        self.send([cmd.id, 0] + data)
        
    def s(self, cmd, data):
        self.send_command(cmd, data)
        
    def send_response(self, cmd, *response):
        self.send([cmd.id, response])
        
    def s_login(self):
        self.send_command(Client.LOGIN, [[1], ['null', '27123457894', '1227982522', 'socket://localhost:9119', 30, 2, 0]])
        
        self.s_presence(*self.user.contacts)
        
        
    def s_msg(self, sender, message, time=0, type=MessageType.TEXT):
        self.send_command(Server.MESSAGE, [[sender.jid, time, type.id], [message]])
        
    def s_presence(self, *contacts):
        bdata = []
        for contact in contacts:
            bdata.append([contact.group, contact.user.jid, contact.alias, contact.user.presence.id, contact.user.type.id, contact.user.mood.id])
        bdata.append([])
        self.send_command(Server.PRESENCE, bdata)
        
    def s_room_invite(self, room):
        contact = self.user.get_contact(room.owner)
        self.send_command(Server.INVITE_RECEIVED, [[room.jid, room.name, room.type.id, 0, 'You have been invited by %s to join the MultiMx %s' % (contact.alias, room.name), contact.alias], ['']])
        
    def s_room_list(self, room):
        names = []
        for user in room.participants:
            if user == self.user:
                names.append(user.name)
            else:
                contact = self.user.get_contact(user)
                if contact:
                    names.append(contact.alias)
                else:
                    names.append(user.name)
        
        self.s_msg(room, 'The following users are in this MultiMx:\n' + '\n'.join(names), type=MessageType.NOTICE)
        
    def create_room(self, name, owner):
        user = User(jid='room12345', name=name, type=BuddyType.ROOM, presence=Presence.AVAILABLE)
        user.participants = [owner]
        user.owner = owner
        self.userlist.add(user)
        return user
        
    def got_data(self, data):
        print repr(data)
        
        command = parse(data)
        print "RECEIVED:", command
        cmd = Client.byid(int(command['cm'][0]))
        uid = command['id'][0]
        if self.user is None:
            if uid in self.userlist:
                self.user = self.userlist[uid]
            else:
                self.user = self.create_user(uid)
                self.userlist.add(self.user)
            
            
        if 'ms' in command:
            cmd_data = command['ms']
        else:
            cmd_data = None
            
        self.handle_manager.handle(cmd, cmd_data)
        
    def create_user(self, jid):
        user = User(jid)
        user.add_contact(UserContact(self.userlist.info))
        user.add_contact(UserContact(self.userlist.banker, group='Services'))
        user.add_contact(UserContact(self.userlist.echo, group='Tests'))
        user.add_contact(UserContact(self.userlist.chatter, group='Tests'))
        user.add_contact(UserContact(self.userlist.chatter2, group='Tests'))
        
        return user
    
    def listen(self):
        running = True
        while running:
            data = self.socket.recv(1024)
            if data:
                self.append(data)
            else:
                self.socket.close()
                running = False
                
                
    def _r_login(self, data):
        pin, version, a, features, cid, b, country, language = data
        
        print "logged in"
        self.s_login()
              
    def _r_message(self, data): 
         jid, msg, type = data
         print jid, msg, type
         
         #self.s_msg(buddy, "You sent: %s" % msg)
         
              
              
                
if __name__ == "__main__":
    userlist = UserList()
    
    def cb_func((client,address)):
        print "Connected to", address
        s = ServerConnection(client, userlist)
        s.listen()
        
   # print [cmdsender.build_command([1, 2, "3", ["jou", 5], 8])]
    
    s = Listener(9119, cb_func)
    if not s.isopen():
        sys.exit(1)
    s.run()
    print "waiting for exit input..."
    #print sys.__stdin__.readline()
    print "quitting..."
    s.stop()
    print "done."